package indi.mozping.ch06.clazzloader;

import java.io.*;

/**
 * @author by mozping
 * @Classname FileClassLoader
 * @Description 自定义文件类加载器，从文件中读取字节码文件，转化为Class对象
 * 继承ClassLoader来自定义类加载器，但是这样需要自己重写findClass方法
 * @Date 2019/9/16 11:20
 */
public class MyClassLoader2 extends ClassLoader {
    private String path;

    /**
     * path是class文件所保存的根路径
     */
    public MyClassLoader2(String path) {
        this.path = path;
    }

//    @Override
//    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
//        return findClass(name);
//    }

    /**
     * findClass完成类加载逻辑，并且在内部通过defineClass将二进制转换为Class对象
     *
     * @param name Java类名称，如果有包名，需要将包名转换为路径
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        //1.获取类的class文件字节数组
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            //2.通过defineClass生成class对象
            return defineClass(null, classData, 0, classData.length);
        }
    }

    /**
     * 获取class文件并转换为字节流的逻辑实现
     *
     * @param className
     * @return
     */
    private byte[] getClassData(String className) {
        //1.读取类文件的字节
        String path = classNameToPath(className);
        System.out.println(path);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            //2.读取类文件的字节码
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesNumRead);
            }
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 类文件的完全路径
     *
     * @param className
     * @return
     */
    private String classNameToPath(String className) {
        return path + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
    }


}